package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.config.VodConstantProperties;
import com.atguigu.tingshu.album.mapper.AlbumInfoMapper;
import com.atguigu.tingshu.album.mapper.AlbumStatMapper;
import com.atguigu.tingshu.album.mapper.TrackInfoMapper;
import com.atguigu.tingshu.album.mapper.TrackStatMapper;
import com.atguigu.tingshu.album.service.TrackInfoService;
import com.atguigu.tingshu.album.service.VodService;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.UploadFileUtil;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.album.TrackStat;
import com.atguigu.tingshu.query.album.TrackInfoQuery;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.album.AlbumTrackListVo;
import com.atguigu.tingshu.vo.album.TrackInfoVo;
import com.atguigu.tingshu.vo.album.TrackListVo;
import com.atguigu.tingshu.vo.album.TrackMediaInfoVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.xiaoymin.knife4j.core.util.Assert;
import com.qcloud.vod.VodUploadClient;
import com.qcloud.vod.model.VodUploadRequest;
import com.qcloud.vod.model.VodUploadResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class TrackInfoServiceImpl extends ServiceImpl<TrackInfoMapper, TrackInfo> implements TrackInfoService {

	@Autowired
	private TrackInfoMapper trackInfoMapper;

	@Autowired
	private VodConstantProperties vodConstantProperties;

	@Autowired
	private TrackStatMapper trackStatMapper;

	@Autowired
	private AlbumInfoMapper albumInfoMapper;

	@Autowired
	private VodService vodService;

	@Autowired
	private AlbumStatMapper albumStatMapper;

	@Autowired
	private UserInfoFeignClient userInfoFeignClient;

	/**
	 * 根据专辑Id查询声音列表
	 *
	 * @param trackListVoPage
	 * @param albumId
	 * @param userId
	 * @return
	 */
	@Override
	public IPage<AlbumTrackListVo> findAlbumTrackPage(Page<AlbumTrackListVo> trackListVoPage, Long albumId, Long userId) {
		//  跟用户专辑类型有关系！ 根据用户的身份！ 根免费试听集数有关系!
		//  1.  先查询有多少条声音;
		IPage<AlbumTrackListVo> pageInfo = trackInfoMapper.selectAlbumTrackPage(trackListVoPage, albumId);
		//  根据专辑Id获取专辑对象;
		AlbumInfo albumInfo = albumInfoMapper.selectById(albumId);
		//  2.  判断这个用户id 是否为空
		if (null == userId) {
			//  说明未登录; 需要判断专辑的类型; 除免费专辑之外，都需要将声音Id 设置为付费(除试听集数 album_info.tracks_for_free);
			if (!SystemConstant.ALBUM_PAY_TYPE_FREE.equals(albumInfo.getPayType())) {
				//  order_num > album_info.tracks_for_free; 需要将这些数据的字段  isShowPaidMark = true;
				//                for (AlbumTrackListVo albumTrackListVo : pageInfo.getRecords()) {
				//                    if (albumTrackListVo.getOrderNum() > albumInfo.getTracksForFree()){
				//                        albumTrackListVo.setIsShowPaidMark(true);
				//                    }
				//                }
				pageInfo.getRecords().stream().filter(albumTrackListVo -> albumTrackListVo.getOrderNum() > albumInfo.getTracksForFree()).forEach(albumTrackListVo -> albumTrackListVo.setIsShowPaidMark(true));
			}
			//  返回数据;
			return pageInfo;
		}
		//  用户Id 不为空
		if (null != userId) {
			//  声明一个变量 是否需要付费;
			boolean isNeedPay = false;
			//  登录了！ 判断专辑类型: vip 免费； 付费;
			if (SystemConstant.ALBUM_PAY_TYPE_VIPFREE.equals(albumInfo.getPayType())) {
				//  vip 免费 -- 判断用户身份:
				Result<UserInfoVo> infoVoResult = userInfoFeignClient.getUserInfo(userId);
				Assert.notNull(infoVoResult, "远程调用获取用户信息失败");
				UserInfoVo userInfoVo = infoVoResult.getData();
				Assert.notNull(userInfoVo, "远程调用获取用户信息失败");
				if (0 == userInfoVo.getIsVip() || (1 == userInfoVo.getIsVip() && userInfoVo.getVipExpireTime().before(new Date()))) {
					//  付费;
					isNeedPay = true;
				}
			} else if (SystemConstant.ALBUM_PAY_TYPE_REQUIRE.equals(albumInfo.getPayType())) {
				//  付费;
				isNeedPay = true;
			}
			//  判断付费情况
			if (isNeedPay) {
				//  当前专辑的哪些声音Id 需要付费！ 6-25; 除去免费试听集数;  (判断这个用户是否购买过专辑或购买过单集声音)
				List<AlbumTrackListVo> albumTrackListPaidList = pageInfo.getRecords().stream().filter(albumTrackListVo -> albumTrackListVo.getOrderNum() > albumInfo.getTracksForFree()).collect(Collectors.toList());
				//  需要付费的声音Id;
				List<Long> trackNeedPaidList = albumTrackListPaidList.stream().map(AlbumTrackListVo::getTrackId).collect(Collectors.toList());
				//  远程调用: map.put(trackId,1); map.put(trackId,0); 1:表示已经购买; 0:表示未购买 setIsShowPaidMark(true);
				Result<Map<Long, Integer>> mapResult = userInfoFeignClient.userIsPaidTrack(albumId,trackNeedPaidList);
				Assert.notNull(mapResult, "远程调用获取用户付费情况失败");
				//  map.put(trackId,1); map.put(trackId,0); 1:表示已经购买; 0:表示未购买 setIsShowPaidMark(true);
				Map<Long, Integer> map = mapResult.getData();
				//  循环遍历集合;
				for (AlbumTrackListVo albumTrackListVo : albumTrackListPaidList) {
					//  boolean flag = map.get(albumTrackListVo.getTrackId()) == 0?true:false;
					//  true; false;
					albumTrackListVo.setIsShowPaidMark(map.get(albumTrackListVo.getTrackId()) == 0);
				}
			}
		}
		//  返回数据
		return pageInfo;
	}

	/**
	 * 上传声音
	 *
	 * @param file
	 * @return
	 */
	@Override
	public Map<String, Object> uploadTrack(MultipartFile file) {
		//初始化一个上传客户端对象
		//使用云 API 密钥初始化 VodUploadClient 实例。
		VodUploadClient client = new VodUploadClient(vodConstantProperties.getSecretId(), vodConstantProperties.getSecretKey());
		//构造上传请求对象
		VodUploadRequest request = new VodUploadRequest();
		//设置媒体本地上传路径。
		String path= UploadFileUtil.uploadTempPath(vodConstantProperties.getTempPath(), file);
		request.setMediaFilePath(path);
		//调用上传方法，传入接入点地域及上传请求。
		try {
			VodUploadResponse response = client.upload(vodConstantProperties.getRegion(), request);
			//如果上传成功，记录日志，输出上传的文件 ID。
			log.info("Upload FileId = {}", response.getFileId());
			//调用数据万象进行声音审核！！！（知道有这一回事）
			//  创建map 对象
			HashMap<String, Object> map = new HashMap<>();
			map.put("mediaFileId", response.getFileId());
			map.put("mediaUrl", response.getMediaUrl());
			//  返回map 数据
			return map;
		} catch (Exception e) {
			// 业务方进行异常处理
			log.error("Upload Err", e);
		}
		return Map.of();
	}

	/**
	 * 保存声音
	 * 前端发送包含音频轨道信息的 JSON 数据。
	 * 后端接收到数据并将其转换为 TrackInfoVo 对象。
	 * 调用 saveTrackInfo 方法，将 TrackInfoVo 中的数据复制到 TrackInfo 实体对象中。
	 * 调用数据库操作方法（如 trackInfoMapper.insert(trackInfo)）将 TrackInfo 对象保存到数据库中。
	 * @param trackInfoVo
	 * @param userId
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void saveTrackInfo(TrackInfoVo trackInfoVo, Long userId) {
		// TODO: 2025/2/23  track_info
		//创建一个新的 TrackInfo 对象。TrackInfo 通常是实体类，用于表示数据库中的一条记录（例如音频轨道的信息）
		TrackInfo trackInfo = new TrackInfo();
		//属性赋值
		// 将 trackInfoVo 中的属性值复制到 trackInfo 对象中
		//这个操作会自动将 trackInfoVo 中所有匹配的属性值复制到 trackInfo 中，简化了手动设置每个属性的过程
		BeanUtils.copyProperties(trackInfoVo, trackInfo);
		//用户id , 前面已经获取到了
		//调用 TrackInfo 类中的 setUserId 方法，将传入的 userId 参数值赋给 trackInfo 对象的 userId 属性。
		trackInfo.setUserId(userId);
		//使用 albumInfoMapper.selectById 方法根据 trackInfo 中的 albumId 获取对应的 AlbumInfo 对象。
		// 这一步是为了获取专辑的相关信息，如当前专辑包含声音总数。
		AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
		//根据专辑中现有的轨道数量，设置新轨道的顺序号为现有数量加1。这样可以确保新添加的轨道按顺序排列
		trackInfo.setOrderNum(albumInfo.getIncludeTrackCount()+1);
		//	media_duration、 media_file_id、 media_url、 media_size media_type;正好对应的实体类TrackMediaInfoVo
		//	这四个字段需要从VOD API中获取，不能直接插入数据库。
		//	必须要调用VOD API 获取真实数据;
		TrackMediaInfoVo trackMediaInfoVo = vodService.getMediaInfo(trackInfoVo.getMediaFileId());
		trackInfo.setMediaSize(trackMediaInfoVo.getSize());
		trackInfo.setMediaDuration(trackMediaInfoVo.getDuration());
		trackInfo.setMediaUrl(trackMediaInfoVo.getMediaUrl());
		trackInfo.setMediaType(trackMediaInfoVo.getType());
		//将 trackInfo 对象插入到数据库中，完成音频轨道信息的保存。
		trackInfoMapper.insert(trackInfo);

		// TODO: 2025/2/23  track_stat
		//每次调用都使用了相同的 trackInfo.getId() 作为轨道 ID 参数，这意味着这些统计信息都是针对同一音频轨道的。
		this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PLAY);//播放次数。
		this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COLLECT);//收藏次数。
		this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_PRAISE);//点赞次数。
		this.saveTrackStat(trackInfo.getId(), SystemConstant.TRACK_STAT_COMMENT);//评论次数。

		// TODO: 2025/2/23  album_info
		//更新专辑信息中的包含轨道数量（includeTrackCount），并将其保存到数据库中。
		//获取当前 albumInfo 对象的 includeTrackCount 属性值，并将其加1。
		//使用 setIncludeTrackCount 方法将更新后的值重新设置回 albumInfo 对象的 includeTrackCount 属性。
		//这一步通常用于在添加新音频轨道时，同步更新专辑中包含的轨道数量。
		albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() + 1);
		//使用 albumInfoMapper.updateById 方法将更新后的 albumInfo 对象保存到数据库中。
		//updateById 方法根据 albumInfo 对象的主键（通常是 id）查找对应的数据库记录，并更新其字段值。
		//这确保了数据库中的专辑信息与内存中的对象保持一致。
		albumInfoMapper.updateById(albumInfo);
	}
	public void saveTrackStat(Long trackId, String trackStatPlay) {
		//创建一个新的 TrackStat 对象，用于表示一条统计记录。
		TrackStat trackStat = new TrackStat();
		//将传入的 trackId 设置到 trackStat 对象的 trackId 属性中，确保该统计记录关联到具体的音频轨道。
		trackStat.setTrackId(trackId);
		//将传入的 trackStatPlay 设置到 trackStat 对象的 statType 属性中，表示此次统计的具体类型（如播放次数、点赞数等）
		trackStat.setStatType(trackStatPlay);
		trackStat.setStatNum(new Random().nextInt(10000000));
		trackStatMapper.insert(trackStat);
	}
	@Override
	public IPage<TrackListVo> findUserTrackPage(Page<TrackListVo> trackListVoPage, TrackInfoQuery trackInfoQuery) {
		return trackInfoMapper.selectUserTrackPage(trackListVoPage, trackInfoQuery);
	}

	/**
	 * 根据声音id删除声音
	 *
	 * @param trackId
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void removeTrackInfo(Long trackId) {
		//  获取到声音对象
		TrackInfo trackInfo = trackInfoMapper.selectById(trackId); //与 TrackInfo trackInfo = this.getById(trackId) 一样
		//  业务分析：track_info track_stat album_info.include_track_count - 1; 云点播里面数据
		//track_info
		trackInfoMapper.deleteById(trackId);
		//track_stat
		trackStatMapper.delete(new LambdaQueryWrapper<TrackStat>().eq(TrackStat::getTrackId, trackId));
		//album_info.include_track_count - 1
		// 减少专辑表的包含声音总数;需要获取到专辑对象（可以通过专辑id获取专辑对象）-> 声音表里有专辑id，所有可以先获取声音对象，然后获取到专辑id。
		//  获取到专辑对象
		AlbumInfo albumInfo = albumInfoMapper.selectById(trackInfo.getAlbumId());
		albumInfo.setIncludeTrackCount(albumInfo.getIncludeTrackCount() - 1);
		albumInfoMapper.updateById(albumInfo);

		//  处理声音序号; update track_info set order_num = order_num - 1 where album_id = 1 and order_num > 50 and is_deleted = 0
		trackInfoMapper.updateOrderNum(albumInfo.getId(), trackInfo.getOrderNum());
		//删除云点播里面数据
		vodService.deleteMedia(trackInfo.getMediaFileId());
	}

	@Override
	public TrackInfoVo getTrackInfo(Long trackId) {
		//根据阿里巴巴开发手册，在数据传输时，我们使用VO做数据回显，使用dto做数据传输，实体类只是做一个基本的数据载体
		//  根据声音Id获取声音对象
		TrackInfo trackInfo = this.getById(trackId);
		//  返回数据
		TrackInfoVo trackInfoVo = new TrackInfoVo();
		//  属性拷贝：
		BeanUtils.copyProperties(trackInfo, trackInfoVo);
		return trackInfoVo;
	}

	/**
	 * 修改声音
	 *
	 * @param trackId
	 * @param trackInfoVo
	 */
	@Override
	public void updateTrackInfo(Long trackId, TrackInfoVo trackInfoVo) {
		//this 关键字表示当前对象实例，即 TrackInfoServiceImpl 类的实例
		//trackId 是一个标识音轨记录的唯一ID，通过这个ID可以从数据源中获取对应的音轨信息。
		TrackInfo trackInfo = this.getById(trackId);
		//获取原来的流媒体Id
		String oldMediaFileId = trackInfo.getMediaFileId();
		//属性拷贝：
		BeanUtils.copyProperties(trackInfoVo, trackInfo);
		//考虑如果修改了流媒体数据;
		String mediaFileId = trackInfoVo.getMediaFileId();
		//判断比较
		if (!oldMediaFileId.equals(mediaFileId)) {
			////  获取最新流媒体数据;
			TrackMediaInfoVo mediaInfo = vodService.getMediaInfo(mediaFileId);
			//将上一步获取到的 mediaInfo 对象中的 duration 属性值设置到 trackInfo 对象的 mediaDuration 属性中
			trackInfo.setMediaDuration(mediaInfo.getDuration());
			trackInfo.setMediaSize(mediaInfo.getSize());
			trackInfo.setMediaType(mediaInfo.getType());
			trackInfo.setMediaUrl(mediaInfo.getMediaUrl());
			//  删除旧的流媒体数据
			vodService.deleteMedia(oldMediaFileId);
		}
		//  表示修改所有数据！
		trackInfoMapper.updateById(trackInfo);

	}

}
